package cn.chiship.sdk.pay.core.util;

import cn.chiship.sdk.core.base.BaseResult;
import cn.chiship.sdk.core.base.constants.BaseConstants;
import cn.chiship.sdk.core.util.StringUtil;
import cn.chiship.sdk.pay.core.config.WxPayV3Config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import com.wechat.pay.java.core.util.PemUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;

/**
 * 微信支付v3网络请求工具
 *
 * @author lijian
 */
public class WxPayV3HttpUtil {

	private static final String ERROR_MSG1 = "微信支付V3请求失败";

	protected static final Logger LOGGER = LoggerFactory.getLogger(WxPayV3HttpUtil.class);

	private static WxPayV3HttpUtil wxPayV3HttpUtil;

	/**
	 * 商户证书私钥
	 */
	private PrivateKey privateKey;

	/**
	 * 证书
	 */
	private Verifier verifier;

	/**
	 * 请求客户端
	 */
	private CloseableHttpClient httpClient;

	private WxPayV3Config wxPayV3Config;

	private WxPayV3HttpUtil() {
	}

	public static synchronized WxPayV3HttpUtil getInstance() {
		if (wxPayV3HttpUtil == null) {
			wxPayV3HttpUtil = new WxPayV3HttpUtil();
		}
		return wxPayV3HttpUtil;
	}

	public WxPayV3HttpUtil config() {
		this.wxPayV3Config = new WxPayV3Config();
		privateKey = PemUtil.loadPrivateKeyFromString(wxPayV3Config.getPrivateKey());
		return this;
	}

	public WxPayV3HttpUtil config(WxPayV3Config wxPayV3Config) {
		this.wxPayV3Config = wxPayV3Config;
		privateKey = PemUtil.loadPrivateKeyFromString(wxPayV3Config.getPrivateKey());
		return this;
	}

	/**
	 * 获取微信证书
	 * @throws Exception
	 */
	private void setVerifier() throws HttpCodeException, GeneralSecurityException, IOException, NotFoundException {
		CertificatesManager certificatesManager = CertificatesManager.getInstance();
		certificatesManager.putMerchant(wxPayV3Config.getMchId(),
				new WechatPay2Credentials(wxPayV3Config.getMchId(),
						new PrivateKeySigner(wxPayV3Config.getSerialNumber(), privateKey)),
				wxPayV3Config.getMchKey().getBytes(StandardCharsets.UTF_8));
		verifier = certificatesManager.getVerifier(wxPayV3Config.getMchId());
	}

	/**
	 * 创建请求客户端
	 * @throws Exception
	 */
	private void setHttpClient() throws GeneralSecurityException, IOException, NotFoundException, HttpCodeException {
		if (verifier == null) {
			setVerifier();
		}
		WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
				.withMerchant(wxPayV3Config.getMchId(), wxPayV3Config.getSerialNumber(), privateKey)
				.withValidator(new WechatPay2Validator(verifier));
		httpClient = builder.build();
	}

	/**
	 * 发送POST请求
	 * @param url 请求地址
	 * @param params json参数
	 * @return BaseResult
	 */
	public BaseResult sendPost(String url, JSONObject params) {
		try {
			if (httpClient == null) {
				setHttpClient();
			}
			HttpPost httpPost = new HttpPost(url);
			httpPost.addHeader("Accept", "application/json");
			httpPost.addHeader("Content-type", "application/json; charset=utf-8");
			httpPost.setEntity(new StringEntity(params.toJSONString(), StandardCharsets.UTF_8));
			CloseableHttpResponse response = httpClient.execute(httpPost);
			return analysisHttpResponse(response);
		}
		catch (Exception e) {
			LOGGER.error(ERROR_MSG1, e);
			return BaseResult.error(ERROR_MSG1);
		}
		finally {
			closeWXClient();
		}
	}

	/**
	 * 发送get请求
	 * @param url 请求地址 参数直接在地址上拼接
	 * @return BaseResult
	 */
	public BaseResult sendGet(String url) {
		try {
			if (httpClient == null) {
				setHttpClient();
			}
			URIBuilder uriBuilder = new URIBuilder(url);
			HttpGet httpGet = new HttpGet(uriBuilder.build());
			httpGet.addHeader("Accept", "application/json");
			CloseableHttpResponse response = httpClient.execute(httpGet);
			return analysisHttpResponse(response);
		}
		catch (Exception e) {
			LOGGER.error(ERROR_MSG1, e);
			return BaseResult.error(ERROR_MSG1);
		}
		finally {
			closeWXClient();
		}
	}

	/**
	 * 关闭 HttpClient
	 */
	public void closeWXClient() {
		if (httpClient != null) {
			try {
				httpClient.close();
				httpClient = null;
			}
			catch (IOException e) {
				LOGGER.error("发生异常", e);
			}
		}
	}

	/**
	 * 解析
	 * @param response
	 * @return BaseResult
	 * @throws IOException
	 */
	public BaseResult analysisHttpResponse(CloseableHttpResponse response) throws IOException {
		return analysisHttpResponse(response, BaseConstants.UTF8);
	}

	/**
	 * 解析
	 * @param response
	 * @param charset
	 * @return BaseResult
	 * @throws IOException
	 */
	public BaseResult analysisHttpResponse(CloseableHttpResponse response, String charset) throws IOException {
		String bodyAsString = EntityUtils.toString(response.getEntity(), charset);
		LOGGER.info("--------->微信返回的内容：" + bodyAsString);
		if (StringUtil.isNullOrEmpty(bodyAsString)) {
			return BaseResult.error("没有返回响应内容");
		}
		JSONObject bodyAsJson = JSON.parseObject(bodyAsString);
		if (bodyAsJson.containsKey("code")) {
			return BaseResult.error(bodyAsJson.getString("message"));
		}
		return BaseResult.ok(bodyAsJson);
	}

	/**
	 * 回调通知验签与解密
	 * @param request
	 * @return BaseResult
	 */
	public BaseResult getCallbackData(HttpServletRequest request) {
		try {
			if (verifier == null) {
				setVerifier();
			}
			String wechatPaySerial = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_SERIAL);
			String nonce = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_NONCE);
			String timestamp = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_TIMESTAMP);
			String signature = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_SIGNATURE);
			String body;
			BufferedReader reader = request.getReader();
			String line;
			StringBuilder inputString = new StringBuilder();
			while ((line = reader.readLine()) != null) {
				inputString.append(line);
			}
			body = inputString.toString();
			reader.close();
			NotificationRequest res = new NotificationRequest.Builder().withSerialNumber(wechatPaySerial)
					.withNonce(nonce).withTimestamp(timestamp).withSignature(signature).withBody(body).build();
			NotificationHandler handler = new NotificationHandler(verifier,
					wxPayV3Config.getMchKey().getBytes(StandardCharsets.UTF_8));
			Notification notification = handler.parse(res);
			LOGGER.info("回调通知数据：" + notification.toString());
			String decryptData = notification.getDecryptData();
			LOGGER.info("回调解析数据：" + decryptData);
			if (StringUtil.isNullOrEmpty(decryptData)) {
				return BaseResult.error("回调通知验签与解密没有任何内容");
			}
			return BaseResult.ok(JSON.parseObject(decryptData));
		}
		catch (Exception e) {
			LOGGER.error("微信支付V3回调失败", e);
			return BaseResult.error("微信支付V3回调失败");
		}
	}

	/**
	 * 微信支付v3签名 RSA签名
	 * @param message 要签名的字符串
	 * @return BaseResult
	 */
	public BaseResult signRSA(String message) {
		try {
			Signer signer = new PrivateKeySigner(wxPayV3Config.getSerialNumber(), privateKey);
			Signer.SignatureResult signature = signer.sign(message.getBytes(StandardCharsets.UTF_8));
			return BaseResult.ok(signature.getSign());
		}
		catch (Exception e) {
			LOGGER.error("微信支付v3 RSA签名签名失败", e);
			return BaseResult.error("微信支付v3 RSA签名签名失败");
		}
	}

}
